package la.iok.hzsvn.lewin.movie.cloud.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import la.iok.hzsvn.lewin.movie.cloud.entity.Device;
import la.iok.hzsvn.lewin.movie.cloud.entity.OrgUnit;
import la.iok.hzsvn.lewin.movie.cloud.entity.Room;
import la.iok.hzsvn.lewin.movie.cloud.mapper.DeviceMapper;
import la.iok.hzsvn.lewin.movie.cloud.model.DeviceParam;
import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.DeviceVo;
import la.iok.hzsvn.lewin.movie.core.model.QR;
import la.iok.hzsvn.lewin.mybatis.model.PageVo;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.collection.IdOperators;
import la.iok.hzsvn.share.utils.CollectionUtils;
import la.iok.hzsvn.share.utils.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DeviceServiceImpl extends BaseServiceImpl<Device,DeviceMapper,Long> implements DeviceService{
    private final OrgUnitService orgUnitService;
    private final RoomService roomService;
    private final QRCodeService qrCodeService;

    public DeviceServiceImpl(OrgUnitService orgUnitService,
                             RoomService roomService,
                             DeviceMapper deviceMapper,
                             QRCodeService qrCodeService) {
        super(deviceMapper);
        this.orgUnitService = orgUnitService;
        this.roomService = roomService;
        this.qrCodeService = qrCodeService;
    }

    @NotNull
    @Override
    public Long save(@NotNull Token token, @NotNull DeviceParam device) {
        String name = ErrorCode.NAME_BLANK_ERROR.assertNotBlank(device.getName());
        String code = ErrorCode.DEVICE_CODE_EMPTY_ERROR.assertNotBlank(device.getCode());
        ErrorCode.ORG_UNIT_EMPTY_ERROR.assertNotNull(device.getOrgUnitId());
        OrgUnit orgUnit = orgUnitService.select(device.getOrgUnitId());
        ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit,device.getOrgUnitId());
        if(token.hasAuthority(RoleEnum.Admin)){
            //都可以
        }else if(token.hasAuthority(RoleEnum.Manager)){
            //只能在自己所管理的店铺或者加盟商中添加
            Set<Long> set = new HashSet<>(orgUnitService.childOrgUnitIds(token.getOrgUnitId()));
            set.add(token.getOrgUnitId());
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertTrue(set.contains(orgUnit.getId()),orgUnit.getName());
        }else if(token.hasAuthority(RoleEnum.ShopManager)){
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(token.getOrgUnitId(),orgUnit.getId(),orgUnit.getName());
        }else{
            ErrorCode.ORG_UNIT_ACCESS_DENIED.fail(orgUnit.getName());
        }
        if(code.startsWith("PC_")){
            //PC播放器必须要有房间
            ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(device.getRoomId() != null && device.getRoomId() > 0,"房间","播放器需要扫描房间二维码注册");
        }
        if(device.getRoomId() != null && device.getRoomId() > 0){
            Room r = roomService.select(device.getRoomId());
            ErrorCode.ROOM_NOT_EXIST.assertNotNull(r,device.getRoomId());
            ErrorCode.ROOM_NOT_BELONG_ORG_UNIT.assertEqual(r.getOrgUnitId(),orgUnit.getId(),r.getName(),orgUnit.getName());
        }
        ErrorCode.DEVICE_ALREADY_EXIST.assertNull(select(code),code);
        Device d = new Device();
        d.setName(name);
        d.setCode(code);
        d.setOrgUnitId(orgUnit.getId());
        d.setRoomId(device.getRoomId());
        mapper.insertSelective(d);
        return d.getId();
    }

    @Override
    public Long qrCodeSave(Token token, DeviceParam device) {
        ErrorCode.SCAN_QR_CODE_2_REGISTER.assertNotBlank(device.getUuid());
        QR qr = qrCodeService.qrCode(device.getUuid());
        if(qr.typeRoom()){
            Room room = roomService.select(qr.getId());
            ErrorCode.ROOM_NOT_EXIST.assertNotNull(room,qr.getId());
            device.setOrgUnitId(room.getOrgUnitId());
            device.setRoomId(room.getId());
        }else{
            device.setOrgUnitId(qr.getId());
            device.setRoomId(null);
        }
        return save(token,device);
    }

    @Nullable
    @Override
    public Device select(@Nullable String code) {
        if(StringUtils.isBlank(code))
            return null;
        return mapper.selectByProperty("code",code);
    }

    @Override
    public void update(@NotNull Token token,@Nullable Long id, @Nullable String name,@Nullable Long roomId,@Nullable Long orgUnitId) {
       if(StringUtils.isNotBlank(name)){
           name = name.trim();
       }
       Device device = select(id);
       ErrorCode.DEVICE_NOT_EXIST.assertNotNull(device,id);
       Room room = roomService.select(roomId);
       if(roomId != null && roomId > 0){
           ErrorCode.ROOM_NOT_EXIST.assertNotNull(room,roomId);
       }
       if(orgUnitId != null && orgUnitId > 0){
           OrgUnit orgUnit = orgUnitService.select(orgUnitId);
           ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit,orgUnitId);
           if(!orgUnitId.equals(device.getOrgUnitId())){
               //修改了组织
               validate(token, device, orgUnit);
           }else{
               //没有修改组织
           }
           if(room != null){
               ErrorCode.ROOM_NOT_BELONG_ORG_UNIT.assertEqual(room.getOrgUnitId(),orgUnit.getId(),room.getName(),orgUnit.getName());
           }
       }else{
           if(room != null){
               OrgUnit orgUnit = orgUnitService.select(room.getOrgUnitId());
               ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit,room.getOrgUnitId());
               if(token.hasAuthority(RoleEnum.ShopManager)){
                   ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(token.getOrgUnitId(),room.getOrgUnitId(),room.getOrgUnitId());
               }else {
                   validate(token, device, orgUnit);
               }
           }
       }
       if(StringUtils.isBlank(name) && (roomId == null || roomId < 0) && (orgUnitId == null || orgUnitId <=0)){
           //都是无效值,不更新
           return;
       }
        mapper.update(id,name,roomId,orgUnitId);
    }

    /**
     * 验证是否拥有设备的组织访问权限，包括设备的源组织和目标组织
     * @param token 操作用户
     * @param device 验证的设备，包含源组织信息
     * @param orgUnit 目标组织
     */
    private void validate(@NotNull Token token, Device device, OrgUnit orgUnit) {
        if(token.hasAuthority(RoleEnum.Admin)){
            //都可以
        }else if(token.hasAuthority(RoleEnum.Manager)){
            HashSet<Long> set = new HashSet<>(orgUnitService.childOrgUnitIds(token.getOrgUnitId()));
            set.add(token.getOrgUnitId());
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertTrue(set.contains(orgUnit.getId()),orgUnit.getName());
            OrgUnit sourceOrgUnit = orgUnitService.select(device.getOrgUnitId());
            ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(sourceOrgUnit,device.getOrgUnitId());
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertTrue(set.contains(sourceOrgUnit.getId()),sourceOrgUnit.getName());
        }else{
            ErrorCode.ORG_UNIT_ACCESS_DENIED.fail(orgUnit.getName());
        }
    }

    @Transactional
    @Override
    public void delete(@NotNull Token token, @Nullable Long id) {
        Device device = select(id);
        ErrorCode.DEVICE_NOT_EXIST.assertNotNull(device,id);

        if(token.hasAuthority(RoleEnum.Admin)){
            //都可以
        }else if(token.hasAuthority(RoleEnum.Manager)){
            OrgUnit orgUnit = orgUnitService.select(device.getOrgUnitId());
            ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit,device.getOrgUnitId());
            Set<Long> set = new HashSet<>(orgUnitService.childOrgUnitIds(token.getOrgUnitId()));
            set.add(token.getOrgUnitId());
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertTrue(set.contains(orgUnit.getId()),orgUnit.getName());
        }else if(token.hasAuthority(RoleEnum.ShopManager)){
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(token.getOrgUnitId(),device.getOrgUnitId(),device.getOrgUnitId());
        }else{
            ErrorCode.ORG_UNIT_ACCESS_DENIED.fail(device.getOrgUnitId());
        }
        delete(id);
    }

    @Override
    public PageVo<DeviceVo> page(int page, int limit, Token token, Long orgUnitId, Long roomId, String name) {
        Page<Device> list = null;
        if(token.hasAuthority(RoleEnum.Admin)){
            list = PageHelper.startPage(page,limit);
            if(orgUnitId != null){
                mapper.list(Collections.singleton(orgUnitId),roomId,name);
            }else{
                mapper.list(null,roomId,name);
            }
        }else{
            Collection<Long> orgUnitIds = new HashSet<>(orgUnitService.childOrgUnitIds(token.getOrgUnitId()));
            orgUnitIds.add(token.getOrgUnitId());
            if(orgUnitId != null && orgUnitId > 0){
                if(orgUnitIds.contains(orgUnitId)){
                    orgUnitIds = Collections.singleton(orgUnitId);
                }else{
                    return new PageVo<>(limit,page);
                }
            }
            if(orgUnitIds.isEmpty()){
                return new PageVo<>(limit,page);
            }
            list = PageHelper.startPage(page,limit);
            mapper.list(orgUnitIds,roomId,name);
        }
        PageVo<DeviceVo> pageVo = new PageVo<>();
        pageVo.setPages(list.getPages());
        pageVo.setTotal(list.getTotal());
        pageVo.setCurrent(list.getPageNum());
        pageVo.setSize(list.getPageSize());
        pageVo.setRecords(po2vo(list));
        return pageVo;
    }

    @Nullable
    @Override
    public DeviceVo po2vo(@Nullable Device entity) {
        if(entity == null){
            return null;
        }
        DeviceVo vo = new DeviceVo();
        BeanUtils.copyProperties(entity,vo);
        OrgUnit orgUnit = orgUnitService.select(entity.getOrgUnitId());
        if(orgUnit != null){
            vo.setOrgUnitName(orgUnit.getName());
        }
        Room room = roomService.select(entity.getRoomId());
        if(room != null){
            vo.setRoomName(room.getName());
        }
        return vo;
    }

    private List<DeviceVo> po2vo(List<Device> devices){
        if(CollectionUtils.isEmpty(devices)){
            return Collections.emptyList();
        }
        Set<Long> orgUnitIds = devices.stream().map(Device::getOrgUnitId).collect(Collectors.toSet());
        Set<Long> roomIds = devices.stream().map(Device::getRoomId).collect(Collectors.toSet());
        List<OrgUnit> orgUnits = orgUnitService.list(orgUnitIds);
        List<Room> rooms = roomService.listInProperty("id",roomIds);
        return devices.stream().map(item->{
            DeviceVo vo = new DeviceVo();
            BeanUtils.copyProperties(item,vo);
            OrgUnit orgUnit = IdOperators.find(orgUnits,item.getOrgUnitId());
            if(orgUnit != null){
                vo.setOrgUnitName(orgUnit.getName());
            }
            Room room = IdOperators.find(rooms,item.getRoomId());
            if(room != null){
                vo.setRoomName(room.getName());
            }
            return vo;
        }).collect(Collectors.toList());
    }
}
